import {
  APP_CONFIG,
  HOSTNAME,
  logger,
  SUPPORTED_PLATFORMS,
} from '../../../types/constants'
import { SYMBOLS } from '../../../types/interfaces'

/**
 * 样式工具类
 */
export class StyleUtils {
  /**
   * 压缩CSS内容
   * @param css - 原始 CSS 内容
   * @returns 压缩后的 CSS 内容
   */
  static minifyCSS(css: string): string {
    try {
      // 1. 移除注释
      let minified = css.replace(/\/\*[\s\S]*?\*\/|([^:]|^)\/\/.*$/gm, '')

      // 2. 移除多余空格和换行
      minified = minified
        .replace(/\s+/g, ' ') // 多个空格合并为一个
        .replace(/\s*([{};:,])\s*/g, '$1') // 移除符号周围的空格
        .replace(/;\}/g, '}') // 移除最后一个分号
        .trim()
      return minified
    }
    catch {
      return css
    }
  }

  /**
   * 压缩JavaScript内容
   * @param js - 原始 JS 内容
   * @returns 压缩后的 JS 内容
   */
  static minifyJS(js: string): string {
    try {
    // 1. 移除单行注释
      let minified = js.replace(/\/\/.*$/gm, '')

      // 2. 移除多行注释
      minified = minified.replace(/\/\*[\s\S]*?\*\//g, '')

      // 3. 移除多余空格和换行
      minified = minified
        .replace(/\s+/g, ' ') // 多个空格合并为一个
        .replace(/\s*([{}();,:])\s*/g, '$1') // 移除符号周围的空格
        .replace(/;\}/g, '}') // 移除最后一个分号
        .replace(/;\s*;/g, ';') // 移除连续分号
        .trim()

      return minified
    }
    catch {
      return js
    }
  }

  /**
   * 注入脚本标签
   * @param scriptTag 脚本标签
   * @param first 是否插入到<head>的第一个子元素
   * @param override 是否覆盖已存在的脚本
   * @param async 是否异步加载
   * @param defer 是否延迟执行
   * @returns 是否注入成功
   */
  static injectScript(
    scriptTag: HTMLScriptElement,
    first: boolean = false,
    override: boolean = false,
    async: boolean = false,
    defer: boolean = false,
  ): boolean {
    const id = scriptTag.getAttribute('id') ?? ''

    // 检查ID是否为空
    if (!id) {
      logger.warn('[SCR] 脚本注入警告: 未指定脚本标签 ID')
      return false
    }

    try {
      // 设置异步和延迟属性
      scriptTag.async = async
      scriptTag.defer = defer

      // 压缩 JS 内容
      if (APP_CONFIG.minifyJS === true && scriptTag.textContent) {
        scriptTag.textContent = this.minifyJS(scriptTag.textContent)
      }

      // 检查是否已存在同 ID 脚本
      const existingScript = document.getElementById(id) as HTMLScriptElement | null
      if (existingScript) {
        if (!override) {
          logger.info(`[SCR] ${id} 脚本已存在且未启用覆盖，跳过注入`)
          return false
        }
        // 移除现有脚本
        existingScript.remove()
        logger.info(`[SCR] ${id} 已移除旧脚本准备替换`)
      }

      // 获取<head>的第一个子元素
      const firstChild = document.head.firstChild

      if (firstChild && first === true) {
      // 插入到第一个子元素之前
        document.head.insertBefore(scriptTag, firstChild)
      }
      else {
      // 直接追加到<head>
        document.head.appendChild(scriptTag)
      }

      logger.info(`[SCR] ${SYMBOLS.DOCUMENTS.SCROLL} ${id} 脚本注入成功`)
      return true
    }
    catch (error: any) {
      logger.error(`[SCR] ${SYMBOLS.DOCUMENTS.SCROLL} ${id} 脚本注入失败:`, error)
      return false
    }
  }

  /**
   * 标准化样式对象（将驼峰转为连字符）
   * @param styles 样式对象
   * @returns 格式化后的样式对象
   */
  static normalizeStyles(styles: Record<string, string>): Record<string, string> {
    return Object.fromEntries(
      Object.entries(styles).map(([prop, value]) => {
        const cssProp = prop.replace(/[A-Z]/g, m => `-${m.toLowerCase()}`)
        return [cssProp, value]
      }),
    )
  }

  /**
   * 将 CSSProperties 对象转换为字符串
   * @param style CSS 样式对象
   * @returns 样式字符串
   */
  static styleToString(style: CSSProperties): string {
    return Object.entries(style)
      .filter(([_, value]) => value !== undefined)
      .map(([key, value]) => {
        const cssKey = key.replace(/[A-Z]/g, m => `-${m.toLowerCase()}`)
        const cssValue = typeof value === 'number' ? `${value}px` : value
        return `${cssKey}:${cssValue}`
      })
      .join(';')
  }

  /**
   * 合并样式字符串
   * @param styles 多个样式字符串
   * @returns 合并后的样式字符串
   */
  static mergeStyles(...styles: string[]): string {
    return styles.filter(Boolean).join(';')
  }

  /**
   * 标记指定样式为百度搜索结果的样式标签（防止被百度搜索二次搜索时清理）
   * @param styleTag 样式标签
   */
  static markBaiduSearchElement(styleTag: HTMLStyleElement | HTMLElement): void {
    if (HOSTNAME === SUPPORTED_PLATFORMS.BAI_DU && !this.isBaiduStyleProtected(styleTag)) {
      styleTag.setAttribute('data-for', 'result')
      styleTag.setAttribute('data-baidu-protected', 'true')
    }
  }

  /**
   * 检查样式标签是否已被标记保护
   */
  static isBaiduStyleProtected(styleTag: HTMLStyleElement | HTMLElement): boolean {
    return styleTag.hasAttribute('data-baidu-protected')
  }

  /**
   * 将类名字符串转换为 CSS 选择器
   */
  static classNameToSelector(className: string, namespace?: string): string {
    if (!className || typeof className !== 'string') {
      logger.warn(`类名为空或非字符串，返回空选择器`)
      return ''
    }

    // 清理类名字符串
    const cleanedClassName = this.escapeClassName(className.trim())

    if (cleanedClassName === '') {
      logger.warn(`类名为空字符串，返回空选择器`)
      return ''
    }

    try {
      // 分割多个类名
      const classNames = cleanedClassName.split(/\s+/).filter(name => this.isValidClassName(name))

      if (classNames.length === 0)
        return ''

      // 应用命名空间
      const namespacedClassNames = namespace
        ? classNames.map(name => `${namespace}-${name}`)
        : classNames

      // 构建选择器
      return namespacedClassNames.map(name => `.${this.escapeClassName(name)}`).join('')
    }
    catch (error) {
      logger.error(`类名转换选择器时出错，返回空选择器`, error)
      return ''
    }
  }

  /**
   * 转义类名中的特殊字符
   * ```
   */
  static escapeClassName(className: string): string {
    // CSS 选择器特殊字符需要转义
    return className.replace(/([!"#$%&'()*+,./:;<=>?@[\\\]^`{|}~])/g, '\\$1')
  }

  /**
   * 验证类名是否有效
   */
  static isValidClassName(className: string): boolean {
    if (!className || typeof className !== 'string')
      return false

    // CSS 类名规范：以字母、下划线或连字符开头，后跟字母、数字、连字符、下划线
    const classNameRegex = /^-?[a-z_][\w-]*$/i
    return classNameRegex.test(className)
  }
}
